// (C) 2022 Intel Corporation. All rights reserved.
// Your use of Intel Corporation's design tools, logic functions and other 
// software and tools, and its AMPP partner logic functions, and any output 
// files from any of the foregoing (including device programming or simulation 
// files), and any associated documentation or information are expressly subject 
// to the terms and conditions of the Intel Program License Subscription 
// Agreement, Intel FPGA IP License Agreement, or other applicable 
// license agreement, including, without limitation, that your use is for the 
// sole purpose of programming logic devices manufactured by Intel and sold by 
// Intel or its authorized distributors.  Please refer to the applicable 
// agreement for further details.

`timescale 1 ns / 1 ps
`default_nettype none

module async_input_filter #(
    parameter NUM_METASTABILITY_REGS    = 2,            // number of registers to add for metastability hardening
    parameter NUM_FILTER_REGS           = 5             // number of registers for filtering glitches (must be >1), input must be stable for this many clocks before the output will switch
) (
    input  wire             clock,                      // master clock for this block
    input  wire             ia_async_in,                // intput to be synchronized and filtered
    output logic            o_sync_out,                 // output synchronized and filtered to remove glitches < NUM_FILTER_REGS clocks in length
    output logic            o_rising_edge,              // asserted for one clock cycle, simultaneous with the first 1 output on sync out after a 0
    output logic            o_falling_edge              // asserted for one clock cycle, simultaneous with the first 0 output on sync out after a 1
);

    logic metastability_reg;
    logic [NUM_FILTER_REGS:1]           filter_regs;
    logic                               filtered_out;
    
    // Metastability registers (includes case where 0 metastability registers are requested)
	altera_std_synchronizer #(
		.depth(NUM_METASTABILITY_REGS)
	)
    u_sync (
		.clk(clock), 
		.reset_n(1'b1), 
		.din(ia_async_in), 
		.dout(metastability_reg)
	);
    
    // filter out glitches
    always_ff @(posedge clock ) begin
    
        filter_regs <= {metastability_reg, filter_regs[NUM_FILTER_REGS:2]};
        
        // only assign a new value to filtered_out when all filtered_regs registers are equal
        if (filter_regs == {NUM_FILTER_REGS{1'b1}}) begin
            filtered_out <= '1;
        end else if (filter_regs == {NUM_FILTER_REGS{1'b0}}) begin
            filtered_out <= '0;
        end else begin
            filtered_out <= filtered_out;       // hold previous value
        end
        
        o_sync_out <= filtered_out;             // one more clock cycle of delay before reaching the output, so the output changes the same time the edge output signals are asserted
        
        // check for rising and falling edges
        
        if( filtered_out & ~o_sync_out ) begin
            o_rising_edge <= '1;
        end else begin
            o_rising_edge <= '0;
        end
        
        if( ~filtered_out & o_sync_out ) begin
            o_falling_edge <= '1;
        end else begin
            o_falling_edge <= '0;
        end
        
    end
    
endmodule